/*
	Siphash implementation using variables
	Elie Bursztein (hello@elie.im)
	Free for any use.
*/

var SipHashVar = (function() {
	
	var v00, v01, v10, v11, v20, v21, v30, v31;
	
    function _compress() {
    	var x;
    	var y;
    	
    	//_add(v0, v1);
    	x = v00 + v10;
    	v00 = x >>> 0;
    	v01 = v01 + v11 + (x / 2 >>> 31) >>> 0;
    	
    	//_add(v2, v3);
    	x = v20 + v30;
    	v20 = x >>> 0;
    	v21 = v21 + v31 + (x / 2 >>> 31) >>> 0;
    	
        //console.log('c2', v20, v21, v30, v31);
        
        
    	//_rotl(v1, 13);
        x = v10 << 13 | v11 >>> (32 - 13);
        v11 = v11 << 13 | v10 >>> (32 - 13);
        v10 = x;
        
    	//_rotl(v3, 16);
        x = v30 << 16 | v31 >>> (32 - 16);
        v31 = v31 << 16 | v30 >>> (32 - 16);
        v30 = x;
    	
        //console.log('c3', v10, v11, v30, v31);
        
        //_xor(v1, v0);
        v11 ^= v01; 
        v11 >>>= 0; //needed ?
        v10 ^= v00;
        v10 >>>= 0; //needed ?
        

        //_xor(v3, v2);
        v31 ^= v21; 
        v31 >>>= 0; //needed ?
        v30 ^= v20;
        v30 >>>= 0; //needed ?
        
        //console.log('c4', v10, v11, v30, v31);
        
        //_rotl32(v0);
        x = v00;
        v00 = v01;
        v01 = x;
        //_add(v2, v1);
    	x = v20 + v10;
    	v20 = x >>> 0;
    	v21 = v21 + v11 + (x / 2 >>> 31) >>> 0;
        
    	//console.log('c5', v00, v01, v20, v21);
    	
        //_add(v0, v3);
    	x = v00 + v30;
    	v00 = x >>> 0;
    	v01 = v01 + v31 + (x / 2 >>> 31) >>> 0;
        
        //_rotl(v1, 17);
        x = v10 << 17 | v11 >>> (32 - 17);
        v11 = v11 << 17 | v10 >>> (32 - 17);
        v10 = x;
    	
    	//console.log(v2[1]);
        
        //_rotl(v3, 21);
        x = v30 << 21 | v31 >>> (32 - 21);
        v31 = v31 << 21 | v30 >>> (32 - 21);
        v30 = x;
        
        
        //_xor(v1, v2);
        v11 ^= v21; 
        v11 >>>= 0;  //needed for unsigned stuff
        v10 ^= v20;
        v10 >>>= 0;   //needed for unsigned stuff
        
        //console.log(v2[1]);
        
        //_xor(v3, v0);
        v31 ^= v01; 
        v31 >>>= 0; //needed for unsigned stuff
        v30 ^= v00;
        v30 >>>= 0; //needed for unsigned stuff
        
        //_rotl32(v2);
        x = v20;
        v20 = v21;
        v21 = x;
        
    }

    function _get_int(a, offset) {
        return a.charCodeAt(offset + 3) << 24 |
               a.charCodeAt(offset + 2) << 16 |
               a.charCodeAt(offset + 1) << 8 |
               a.charCodeAt(offset);
    }

    function hash(key, m) {            
        v00 = key[0] >>> 0 ^ 0x70736575,
        v01 = key[1] >>> 0 ^ 0x736f6d65, 
        v10 = key[2] >>> 0 ^ 0x6e646f6d,
        v11 = key[3] >>> 0 ^ 0x646f7261,
        v20 = key[0] >>> 0 ^ 0x6e657261,
        v21 = key[1] >>> 0 ^ 0x6c796765,
        v30 = key[2] >>> 0 ^ 0x79746573,
        v31 = key[3] >>> 0 ^ 0x74656462;
        /*
        v00  >>>= 0;
        v01  >>>= 0;
        v10  >>>= 0;
        v11  >>>= 0;
        v20  >>>= 0;
        v21  >>>= 0;
        v30  >>>= 0;
        v31  >>>= 0;
        */
        var m00, m01, mp = 0, ml = m.length, ml7 = ml - 7,
        buf = new Uint8Array(new ArrayBuffer(8)), ic = 0;

        //console.log('si', v00, v01, v10, v11, v20, v21, v30, v31);
        while (mp < ml7) {
            m00 = _get_int(m, mp);
            m01 = _get_int(m, mp + 4);
            v30 ^= m00;
            v30 >>>= 0;
            v31 ^= m01;
            v31 >>>= 0;
            _compress();
            _compress();
            //console.log('s3', v00, v01, v10, v11, v20, v21, v30, v31);
            v00 ^= m00; 
            v00 >>>= 0;
            v01 ^= m01;
            v01 >>>= 0;
            mp += 8;
        }
        buf[7] = ml;

        while (mp < ml) {
            buf[ic++] = m.charCodeAt(mp++);
        }
        //fixme optimize the loop
        while (ic < 7) {
            buf[ic++] = 0;
        }
        
       //console.log('s5', v00, v01, v10, v11, v20, v21, v30, v31);
        m00 =  buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0];
        m01 =  buf[7] << 24 | buf[6] << 16 | buf[5] << 8 | buf[4];
        v30 ^= m00;
        v30 >>>= 0;
        v31 ^= m01;
        v31 >>>= 0;
        
       _compress();
       _compress();
       //console.log('s7', v00, v01, v10, v11, v20, v21, v30, v31);
       
       v00 ^= m00;
       v00 >>>= 0;
       v01 ^= m01;
       v01 >>>= 0;
       v20 ^= 0xff;
       v20 >>>= 0;
       //v21 ^= 0; //useless op

       _compress();
       _compress();
       _compress();
       _compress();
       //console.log('sf', v00, v01, v10, v11, v20, v21, v30, v31);
       
       /*
        var h = v0;
       _xor(h, v1);
       _xor(h, v2);
       _xor(h, v3);
       return h;*/
       m00 = v00^v10^v20^v30;
       //m00 >>>= 0;
       m01 = v01^v11^v21^v31;
       //m01 >>>= 0;
       return [m00 >>>0, m01>>>0]; 
    }

    function string16_to_key(a) {
        return [_get_int(a, 0), _get_int(a, 4),
                _get_int(a, 8), _get_int(a, 12)];
    }

    function hash_hex(key, m) {
        var r = hash(key, m);
        return ("0000000" + r[1].toString(16)).substr(-8) +
               ("0000000" + r[0].toString(16)).substr(-8);
    }

    function hash_uint(key, m) {
        var r = hash(key, m);
        return (r[1] & 0x1fffff) * 0x100000000 + r[0];
    }

    return {
        string16_to_key: string16_to_key,
        hash: hash,
        hash_hex: hash_hex,
        hash_uint: hash_uint
    };
})();